﻿using JT1078.Gateway.Abstractions;
using JT1078.Gateway.Configurations;
using JT1078.Gateway.Metadata;
using JT1078.Gateway.Sessions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JT1078.Gateway.Extensions;
using JT1078.Gateway.Services;
using System.Security.Claims;
using System.Linq;
using Newtonsoft.Json;

namespace JT1078.Gateway
{
    /// <summary>
    /// http服务器
    /// </summary>
    public class JT1078HttpServer : IHostedService
    {
        private readonly ILogger Logger;

        private readonly JT1078Configuration Configuration;
        private readonly ServerPublicOptions serverPublicOptions;

        private readonly IJT1078Authorization authorization;

        private HttpListener listener;

        private JT1078HttpSessionManager SessionManager;

        private IVehicleManager VehicleManager;

        private ITerminalCommandPublisher terminalCommandPublisher;
        private TerminalCommandBuild terminalCommandBuild;
        private MediaQueryCompleteCacheManager MediaQueryCompleteCacheManager { get; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="jT1078ConfigurationAccessor"></param>
        /// <param name="serverPublicOptionsAccessor"></param>
        /// <param name="authorization"></param>
        /// <param name="sessionManager"></param>
        /// <param name="vehicleManager"></param>
        /// <param name="loggerFactory"></param>
        /// <param name="terminalCommandPublisher"></param>
        /// <param name="terminalCommandBuild"></param>
        /// <param name="mediaQueryCompleteCacheManager"></param>
        public JT1078HttpServer(
            IOptions<JT1078Configuration> jT1078ConfigurationAccessor,
            IOptions<ServerPublicOptions> serverPublicOptionsAccessor,
            IJT1078Authorization authorization,
            JT1078HttpSessionManager sessionManager,
            IVehicleManager vehicleManager,
            ILoggerFactory loggerFactory,
            ITerminalCommandPublisher terminalCommandPublisher,
            TerminalCommandBuild terminalCommandBuild, MediaQueryCompleteCacheManager mediaQueryCompleteCacheManager)
        {
            Logger = loggerFactory.CreateLogger<JT1078TcpServer>();
            Configuration = jT1078ConfigurationAccessor.Value;
            serverPublicOptions = serverPublicOptionsAccessor.Value;
            this.authorization = authorization;
            this.SessionManager = sessionManager;
            this.VehicleManager = vehicleManager;
            this.terminalCommandPublisher = terminalCommandPublisher;
            this.terminalCommandBuild = terminalCommandBuild;
            MediaQueryCompleteCacheManager = mediaQueryCompleteCacheManager;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (!HttpListener.IsSupported)
            {
                Logger.LogWarning("Windows XP SP2 or Server 2003 is required to use the HttpListener class.");
                return Task.CompletedTask;
            }
            listener = new HttpListener();
            listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
            try
            {
                listener.Prefixes.Add($"http://*:{Configuration.HttpPort}/");
                listener.Start();
            }
            catch (System.Net.HttpListenerException ex)
            {
                Logger.LogWarning(ex, $"{ex.Message}:使用cmd命令[netsh http add urlacl url=http://*:{Configuration.HttpPort}/ user=Everyone]");
                return Task.CompletedTask;
            }
            VehicleManager.LoadVehicles();

            Logger.LogInformation($"JT1078 Http Server start at {IPAddress.Any}:{Configuration.HttpPort}.");
            Task.Factory.StartNew(async () =>
            {
                while (listener.IsListening)
                {
                    var context = await listener.GetContextAsync();
                    try
                    {
                        await Task.Run(async () =>
                        {
                            if (Logger.IsEnabled(LogLevel.Information))
                            {
                                Logger.LogInformation($"[Http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}-{context.Request.RawUrl}");
                            }
                            if (context.Request.RawUrl.StartsWith("/favicon.ico"))
                            {
                                context.Http404();
                                return;
                            }
                            if (context.TryGetGovInfo(out GovRequestDto govInfo)) // 实时视频播放
                            {
                                var vehicle = VehicleManager.GetVehicleByCph(govInfo.Cph);
                                if (vehicle == null)
                                {
                                    context.Http404();
                                    return;
                                }
                                govInfo.SimNo = vehicle.SimNo;

                                if (govInfo.TokenInfo.ApiType == "1")
                                {
                                    var command = terminalCommandBuild.BuildLiveCommand(new LiveDto()
                                    {
                                        SimNo = govInfo.SimNo,
                                        ChannelNo = (byte)int.Parse(govInfo.ChannelNo),
                                        DataType = govInfo.GetJt1078LiveDataType(),
                                        StreamType = 0,
                                        ServerIp = serverPublicOptions.IP,
                                        TcpPort = (ushort)serverPublicOptions.TcpPort,
                                        UdpPort = 0
                                    });
                                    await terminalCommandPublisher.PublishAsync(new BaseEventArgs() { Id = govInfo.SimNo, Bytes = command.Command });
                                    ProcessFlv(context, govInfo);
                                }
                                else if (govInfo.TokenInfo.ApiType == "2") // 历史播放
                                {
                                    if (vehicle == null)
                                    {
                                        context.Http404();
                                        return;
                                    }
                                    govInfo.SimNo = vehicle.SimNo;

                                    var command = terminalCommandBuild.BuildVodCommand(new VodDto()
                                    {
                                        SimNo = govInfo.SimNo,
                                        ChannelNo = (byte)int.Parse(govInfo.ChannelNo),
                                        MediaType = (byte)int.Parse(govInfo.AvType),
                                        ServerIp = serverPublicOptions.IP,
                                        BeginTime = DateTime.Parse(govInfo.TokenInfo.STime),
                                        EndTime = DateTime.Parse(govInfo.TokenInfo.ETime),
                                        TcpPort = (ushort)serverPublicOptions.TcpPort,
                                        StreamType = 0,
                                        MemoryType = 0,
                                        PlaybackWay = 0,
                                        PlaySpeed = 0,
                                        UdpPort = 0
                                    });

                                    await terminalCommandPublisher.PublishAsync(new BaseEventArgs() { Id = govInfo.SimNo, Bytes = command.Command });

                                    ProcessFlv(context, govInfo);
                                }
                                else if (govInfo.TokenInfo.ApiType == "4") //历史视频文件查询
                                {
                                    if (vehicle == null)
                                    {
                                        context.Http404();
                                        return;
                                    }
                                    govInfo.SimNo = vehicle.SimNo;

                                    var command = terminalCommandBuild.BuildQueryMediaFileCommand(new QueryMediaFileDto()
                                    {
                                        SimNo = govInfo.SimNo,
                                        ChannelNo = (byte)int.Parse(govInfo.ChannelNo),
                                        BeginTime = DateTime.Parse(govInfo.TokenInfo.STime),
                                        EndTime = DateTime.Parse(govInfo.TokenInfo.ETime),
                                        StreamType = 0,
                                        MemoryType = 0,
                                        AVResourceType = int.Parse(govInfo.AvType)
                                    });


                                    await terminalCommandPublisher.PublishAsync(new BaseEventArgs() { Id = govInfo.SimNo, Bytes = command.Command });

                                    ProcessQuery(context, govInfo, command.SerialNo);
                                }
                                else if (govInfo.TokenInfo.ApiType == "5") //下载 和 历史播放完全一致
                                {
                                    if (vehicle == null)
                                    {
                                        context.Http404();
                                        return;
                                    }
                                    govInfo.SimNo = vehicle.SimNo;
                                    var command = terminalCommandBuild.BuildVodCommand(new VodDto()
                                    {
                                        SimNo = govInfo.SimNo,
                                        ChannelNo = (byte)int.Parse(govInfo.ChannelNo),
                                        MediaType = (byte)int.Parse(govInfo.AvType),
                                        ServerIp = serverPublicOptions.IP,
                                        BeginTime = DateTime.Parse(govInfo.TokenInfo.STime),
                                        EndTime = DateTime.Parse(govInfo.TokenInfo.ETime),
                                        TcpPort = (ushort)serverPublicOptions.TcpPort,
                                        StreamType = 0,
                                        MemoryType = 0,
                                        PlaybackWay = 0,
                                        PlaySpeed = 0,
                                        UdpPort = 0
                                    });

                                    await terminalCommandPublisher.PublishAsync(new BaseEventArgs() { Id = govInfo.SimNo, Bytes = command.Command });
                                    ProcessFlv(context, govInfo);
                                }
                            }
                            else
                            {
                                await context.Http400();
                                return;
                            }
                            // else
                            //{
                            //    await context.Http401();
                            //}
                        });
                    }
                    catch (Exception ex)
                    {
                        await context.Http500();
                        Logger.LogError(ex, $"[Http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}-{context.Request.RawUrl}-{ex.StackTrace}");
                    }
                }
            }, cancellationToken);
            return Task.CompletedTask;
        }
        private async void ProcessQuery(HttpListenerContext context, GovRequestDto govRequestDto, int serialNo)
        {
            try
            {
                var principal = new ClaimsPrincipal(new GenericIdentity(govRequestDto.TokenInfo.GovTag));
                var jT1078HttpContext = new JT1078HttpContext(context, principal);

                int i = 0;
                while (i < 300) //等待5分钟
                {
                    var s = MediaQueryCompleteCacheManager.Get(serialNo.ToString());
                    if (s == null)
                    {
                        i++;
                        await Task.Delay(1000);
                        continue;
                    }

                    var result = new SearchResult()
                    {
                        PlateMumber = govRequestDto.Cph,
                        SearchStartTime = govRequestDto.TokenInfo.STime,
                        SearchEndTime = govRequestDto.TokenInfo.ETime,

                    };
                    if (s.Order?.Status == 1)
                    {
                        result.FileList = s.Order.VideoList.FileList.Select(c => new FileListItem
                        {
                            Chn = c.Channel,
                            FileSize = c.FileSize,
                            FileStartTime = c.StartTime.ToString("yyyy-MM-dd HH:mm:ss"),
                            FileEndTime = c.EndTime.ToString("yyyy-MM-dd HH:mm:ss"),
                            FilaName = $"rea/a1/fly{c.MediaType}.ifv"
                        }).ToList();
                    }
                    var bodyJson = JsonConvert.SerializeObject(result);
                    var body = Encoding.UTF8.GetBytes(bodyJson);
                    context.Response.ContentType = "application/json";
                    context.Response.StatusCode = (int)HttpStatusCode.OK;
                    await context.Response.OutputStream.WriteAsync(body);
                    context.Response.OutputStream.Close();
                    context.Response.Close();
                    i = 300;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "处理查询视频列表时出错");
            }
            await Task.CompletedTask;
        }




        private async void ProcessFlv(HttpListenerContext context, GovRequestDto govRequestDto)
        {
            ProccessHttpKeepLive(context, govRequestDto, RTPVideoType.Http_Flv);
            await Task.CompletedTask;
        }

        private void ProccessHttpKeepLive(HttpListenerContext context, GovRequestDto govRequestDto, RTPVideoType videoType)
        {
            var principal = new ClaimsPrincipal(new GenericIdentity(govRequestDto.TokenInfo.GovTag));
            var jT1078HttpContext = new JT1078HttpContext(context, principal);
            jT1078HttpContext.RTPVideoType = videoType;
            jT1078HttpContext.Sim = govRequestDto.SimNo;
            jT1078HttpContext.ChannelNo = int.Parse(govRequestDto.ChannelNo);
            jT1078HttpContext.ClientInfo = new HttpClientInfo()
            {
                ApiType = govRequestDto.TokenInfo.ApiType,
                GovTag = govRequestDto.TokenInfo.GovTag,
                Timestamp = govRequestDto.TokenInfo.Timestamp,
                ETime = govRequestDto.TokenInfo.STime,
                STime = govRequestDto.TokenInfo.ETime
            };
            SessionManager.TryAdd(jT1078HttpContext);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            try
            {
                Logger.LogInformation($"JT1078 Http Server stop at {IPAddress.Any}:{Configuration.HttpPort}.");
                SessionManager.TryRemoveAll();
                listener.Stop();
            }
            catch (System.ObjectDisposedException ex)
            {

            }
            catch (Exception ex)
            {
                Logger.LogError(ex, $"JT1078 Http Server error at {IPAddress.Any}:{Configuration.HttpPort}.");
            }
            return Task.CompletedTask;
        }
    }

    public class SearchResult
    {
        public string PlateMumber { get; set; }
        public string SearchStartTime { get; set; }

        public string SearchEndTime { get; set; }

        public List<FileListItem> FileList { get; set; } = new List<FileListItem>();
    }

    public class FileListItem
    {
        public int Chn { get; set; }
        public string FileStartTime { get; set; }
        public string FileEndTime { get; set; }
        public uint FileSize { get; set; }

        public string FilaName { get; set; }
    }
}
